package frame.validateProcessor;

import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import frame.common.Message;
import frame.exception.FrameException;
import frame.log.Log;
import frame.security.entity.User;

/**
 * 拦截进入service的方法，如果发现有没通过validator校验的异常，则直接返回异常代码。
 * 如果service层抛出异常，则扑捉异常，并做相应处理。
 * 
 * 在这个类中集成validator统一校验处理，系统日志统一处理。
 * 
 * 异常处理原则：所以的系统能过通过代码处理的异常都应该在service层内处理。
 * 所以的在service层抛出的异常都要以消息的形式通知ui端的用户。
 * 
 * 异常处理需要移到CustomExceptionHandler中，因为这个切面和spring的声明式事务处理切面处于同一个切出入点
 * 而且一个是通过文件配置做的切入，一个是通过代码注解做的切入，若在这个类中拦截异常加以处理的话，异常就无法被
 * 事务处理知道，同时回归这个类的本质，按照单一责任原则，这个类定义的切面只处理请求参数的校验结果，相当于一个校验层
 * 而错误处理应该移到其他地方做。
 * @author MR.CUIPENG
 *
 */
@Aspect
public class DefaultValidateProcessor {

	private Logger logger = Logger.getLogger(getClass());//记录用户访问日志。
	//下面的切入点是所有的由RequestMapping来实现路径映射的方法。
	//应该换成所有的@controller类下的public方法。但不知道写。
	//execution(@org.springframework.web.bind.annotation.RequestMapping * *(..))
	@Around("execution(@org.springframework.web.bind.annotation.RequestMapping * *(..))")
	public Object around(ProceedingJoinPoint  jpoint){
		Session session=SecurityUtils.getSubject().getSession();
		User user=(User)session.getAttribute("current_user");
		Object[] args=jpoint.getArgs();
		int len=args.length;
		Object obj=null;
		boolean hasbindingResult=false;
		try {
			if(len>0){
				//方法参数个数不为零时，检查是否包含 BindingResult，捕捉validator校验异常，
				//如果有直接返回异常信息，如果没有异常，则继续进入service方法。
				for (int i = 0; i <len ; i++) {//寻找异常信息
					if(args[i] instanceof BindingResult){
						hasbindingResult=true;
						BindingResult br=(BindingResult)args[i];
						if(br.getErrorCount()>0){
							FieldError fe=br.getFieldError();
							Message msg=new Message(fe.getDefaultMessage());
							msg.setReplacer(fe.getField());//添加当前问题变量
							msg.setReplacer(fe.getArguments());//添加该消息用到的参数
							obj= msg;
						}else{
							obj = jpoint.proceed();
						}
					}
				}
				if(!hasbindingResult){//如果参数中不包含BindingResult，则认为不进行校验，直接进入方法。
					obj = jpoint.proceed();
				}
			}else{//如果被拦截的参数个数是0，则直接进入方法。
				obj = jpoint.proceed();
			}
			
			//fuck the Throwable and Error classes should not be caught
		} catch (Throwable  e) {
			//这里捕获的异常信息只能再抛出，因为声明式事务控制需要抛出的异常信息。
			FrameException fe= new FrameException(e);
			fe.setArguments(args);
			throw fe;
		} 
		
		if(obj==null){
			obj=Message.OK;
		}
		
		logger.info(new Log(user,args,obj));
		return obj;
	} 

	

}
